package cn.lingyangwl.agile.dynamic.client;

import cn.hutool.core.util.StrUtil;
import cn.lingyangwl.agile.dynamic.client.event.DynamicConfigEvent;
import cn.lingyangwl.agile.dynamic.config.core.ConfigFactory;
import cn.lingyangwl.agile.model.constants.ConfigCons;
import cn.lingyangwl.agile.model.enums.ConfigDefineEnum;
import cn.lingyangwl.agile.model.module.config.ConfigInfo;
import cn.lingyangwl.agile.model.module.tenant.TenantContext;
import cn.lingyangwl.framework.core.utils.spring.SpringUtils;
import cn.lingyangwl.framework.tool.core.StringUtils;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author shenguangyang
 */
@Slf4j
public abstract class AbstractClientProcess {
    /**
     * key: 租户id_{@link ConfigDefineEnum}
     */
    protected static Map<String, DynamicClient> cache = new ConcurrentHashMap<>();

    private static final Map<ConfigDefineEnum, AbstractClientProcess> dynamicProcessMap = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() {
        dynamicProcessMap.put(configDefine(), this);
    }

    protected static AbstractClientProcess getClientProcess(ConfigDefineEnum configDefine) {
        if (Objects.isNull(configDefine)) {
            throw new BizException("请传入配置定义");
        }
        return dynamicProcessMap.get(configDefine);
    }

    /**
     * 配置key
     * @return {@link ConfigCons}
     */
    public abstract ConfigDefineEnum configDefine();

    public DynamicClient getClient() {
        return getClient(false);
    }

    /**
     *
     * @param fromMainTenant 是否来自主租户配置
     */
    protected DynamicClient getClient(boolean fromMainTenant) {
        TenantContext tenantContext = TenantContext.get();
        String tenantId = fromMainTenant ? tenantContext.getMainTenantId(): tenantContext.getTenantId();
        return cache.get(getCacheKey(tenantId));
    }

    protected String getCacheKey(String tenantId) {
        return tenantId + "_" + configDefine().name();
    }

    protected abstract DynamicClient createClient(String tenantId, String paramConfig);
    protected void delete(String tenantId) {
        cache.remove(getCacheKey(tenantId));
    }

    public static DynamicClient getDynamicClient(ConfigDefineEnum configDefine) {
        return getDynamicClient(configDefine, false);
    }

    /**
     *
     * @param fromMainTenant 是否来自主租户配置
     */
    public static DynamicClient getDynamicClient(ConfigDefineEnum configDefine, boolean fromMainTenant) {
        try {
            AbstractClientProcess clientProcess = AbstractClientProcess.getClientProcess(configDefine);
            TenantContext tenantContext = TenantContext.get();
            String tenantId = fromMainTenant ? tenantContext.getMainTenantId(): tenantContext.getTenantId();

            String cacheKey = tenantId + "_" + configDefine.name();
            DynamicClient client = cache.get(cacheKey);
            if (Objects.isNull(client)) {
                synchronized (cacheKey.intern()) {
                    client = cache.get(cacheKey);
                    if (Objects.nonNull(client)) {
                        return client;
                    }
                    ConfigFactory configFactory = SpringUtils.getBean(ConfigFactory.class);
                    ConfigInfo config = configFactory.getConfigOfNonNull(configDefine);
                    if (Objects.isNull(config) || StringUtils.isEmpty(config.getConfigKey()) ||
                            StrUtil.equalsAny(config.getConfigKey(), "{}", "[]")) {
                        throw new BizException("客户端尚未配置或处于配置中");
                    }

                    client = clientProcess.createClient(tenantId, config.getConfigValue());
                    if (Objects.isNull(client)) {
                        throw new BizException("初始化客户端失败");
                    }
                    cache.put(cacheKey, client);
                    // 发出事件, 方便根据不同业务进行扩展
                    SpringUtils.getContext().publishEvent(new DynamicConfigEvent(client));
                }
            }
            return client;
        } catch (Exception e) {
            if (e instanceof BizException) {
                throw (BizException)e;
            }
            log.error("error: ", e);
            throw new BizException("获取客户端失败");
        }

    }
}
